#include "OptionType.h"
using namespace inilib;


// -----------------------------------------------------------------------
//
// Operators
//
// -----------------------------------------------------------------------


std::istream & inilib::operator>> ( std::istream & is, bool & value )
{
	std::string s;
	std::getline( is, s );
	if (
		s == "1" ||
		s == "t" ||
		s == "y" ||
		s == "on" ||
		s == "yes" ||
		s == "enabled"
		)
	{
		value = true;
	}
	else if (
		s == "0" ||
		s == "f" ||
		s == "n" ||
		s == "off" ||
		s == "no" ||
		s == "disabled"
		)
	{
		value = false;
	}
	else
	{
		is.setstate( std::ios::failbit );
	}

	return is;
}


std::istream & inilib::operator>> ( std::istream & is, short & value )
{
	long long num = 0;
	inilib::operator>>( is, num );
	if (
		num < std::numeric_limits< short >::lowest() ||
		num > std::numeric_limits< short >::max()
		)
	{
		is.setstate( std::ios::failbit );
	}

	value = (short)num;
	return is;
}


std::istream & inilib::operator>> ( std::istream & is, int & value )
{
	long long num = 0;
	inilib::operator>>( is, num );
	if (
		num < std::numeric_limits< int >::lowest() ||
		num > std::numeric_limits< int >::max()
		)
	{
		is.setstate( std::ios::failbit );
	}

	value = (int)num;
	return is;
}


std::istream & inilib::operator>> ( std::istream & is, long & value )
{
	long long num = 0;
	inilib::operator>>( is, num );
	if (
		num < std::numeric_limits< long >::lowest() ||
		num > std::numeric_limits< long >::max()
		)
	{
		is.setstate( std::ios::failbit );
	}

	value = (long)num;
	return is;
}


std::istream & inilib::operator>> ( std::istream & is, long long & value )
{
	// handle sign:
	bool minus = false;
	if ( is.peek() == '+' )
	{
		is.get();
	}
	else if ( is.peek() == '-' )
	{
		is.get();
		minus = true;
	}

	// call appropriate operator:
	if ( is.peek() == '0' )
	{
		is.get();
		if ( is.peek() == 'x' )
		{
			is.get();
			is.setf( std::ios::hex, std::ios::basefield );
			is.operator>>( value );
		}
		else if ( is.peek() == 'p' )
		{
			is.get();
			value = 0;
			while ( is.peek() != EOF )
			{
				int n = is.get() - '0';
				value = 2*value + n;
			}
		}
		else if ( isdigit( is.peek() ) )
		{
			is.setf( std::ios::oct, std::ios::basefield );
			is.operator>>( value );
		}
	}
	else
	{
		is.setf( std::ios::dec, std::ios::basefield );
		is.operator>>( value );
	}

	if ( minus )
	{
		value *= -1;
	}
	return is;
}


std::istream & inilib::operator>> ( std::istream & is, float & value )
{
	return is.operator>>( value );
}


std::istream & inilib::operator>> ( std::istream & is, double & value )
{
	return is.operator>>( value );
}


std::istream & inilib::operator>> ( std::istream & is, long double & value )
{
	return is.operator>>( value );
}


std::istream & inilib::operator>> ( std::istream & is, std::string & value )
{
	// NOTE: don't call operator>> (it will "kill" all whitespace characters)
	std::getline( is, value );
	return is;
}


std::istream & inilib::operator>> ( std::istream & is, unsigned short & value )
{
	unsigned long long num = 0;
	inilib::operator>>( is, num );
	if (
		num < std::numeric_limits< unsigned short >::lowest() ||
		num > std::numeric_limits< unsigned short >::max()
		)
	{
		is.setstate( std::ios::failbit );
	}

	value = (unsigned short)num;
	return is;
}


std::istream & inilib::operator>> ( std::istream & is, unsigned int & value )
{
	unsigned long long num = 0;
	inilib::operator>>( is, num );
	if (
		num < std::numeric_limits< unsigned int >::lowest() ||
		num > std::numeric_limits< unsigned int >::max()
		)
	{
		is.setstate( std::ios::failbit );
	}
	value = (unsigned int)num;
	return is;
}


std::istream & inilib::operator>> ( std::istream & is, unsigned long & value )
{
	unsigned long long num = 0;
	inilib::operator>>( is, num );
	if (
		num < std::numeric_limits< unsigned long >::lowest() ||
		num > std::numeric_limits< unsigned long >::max()
		)
	{
		is.setstate( std::ios::failbit );
	}
	value = (unsigned long)num;
	return is;
}


std::istream & inilib::operator>> ( std::istream & is, unsigned long long & value )
{
	// handle sign:
	bool minus = false;
	if ( is.peek() == '+' )
	{
		is.get();
	}
	else if ( is.peek() == '-' )
	{
		is.get();
		minus = true;
	}

	// call appropriate operator:
	if ( is.peek() == '0' )
	{
		is.get();
		if ( is.peek() == 'x' )
		{
			is.get();
			is.setf( std::ios::hex, std::ios::basefield );
			is.operator>>( value );
		}
		else if ( is.peek() == 'p' )
		{
			is.get();
			value = 0;
			while ( is.peek() != EOF )
			{
				int n = is.get() - '0';
				value = 2*value + n;
			}
		}
		else if ( isdigit( is.peek() ) )
		{
			is.setf( std::ios::oct, std::ios::basefield );
			is.operator>>( value );
		}
	}
	else
	{
		is.setf( std::ios::dec, std::ios::basefield );
		is.operator>>( value );
	}

	if ( minus )
	{
		value *= -1;
	}
	return is;
}


// -----------------------------------------------------------------------
//
// OptionInfo
//
// -----------------------------------------------------------------------


OptionType::OptionType (
	const std::string & name,
	Mandatory isMandatory,
	ValuesAmount amount,
	char separator
)
{
	m_Name = name;
	m_IsMandatory = isMandatory;
	m_Amount = amount;
	m_Separator = separator;
}


OptionType::~OptionType ()
{
}


std::string OptionType::GetName () const
{
	return m_Name;
}


char OptionType::GetSeparator () const
{
	return m_Separator;
}


bool OptionType::IsMandatory () const
{
	return ( m_IsMandatory == MANDATORY );
}


bool OptionType::AreValuesValid ( const std::vector< std::string > & values, unsigned int totalValues ) const
{
	if ( m_Amount == SINGLE_VALUE && totalValues > 1 )
	{
		return false;
	}

	return AreValuesValid_( values );
}